package edu.uic.cs.cs582.process.impl;

import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import org.apache.log4j.Logger;

import edu.mit.jwi.item.POS;
import edu.uic.cs.cs582.data.Component;
import edu.uic.cs.cs582.data.Polarity;
import edu.uic.cs.cs582.data.Word;
import edu.uic.cs.cs582.misc.Assert;
import edu.uic.cs.cs582.misc.LogHelper;
import edu.uic.cs.cs582.process.CnfGenerator;
import edu.uic.cs.cs582.process.LogicFormulaProcessor;
import edu.uic.cs.cs582.process.impl.SatIndexParser.Index;
import edu.uic.cs.cs582.process.impl.SatIndexParser.SynsetIndex;

public class LogicFormulaProcessorImpl extends AbstractVariablesOperations
		implements LogicFormulaProcessor
{
	private final Logger LOGGER = LogHelper
			.getLogger(LogicFormulaProcessorImpl.class);

	//	private boolean constrainMajoritySynsetsSize = (Config.MAX_SUPPORT_MAJORITY_SYNSETS_NUMBER != 0);

	//	protected CnfGenerator majoritySynsetsCnfGenerator = null;
	private CnfGenerator addingFrequencyCnfGenerator = null;

	public LogicFormulaProcessorImpl()
	{
		super(null);
	}

	protected void initializeCnfGenerator(Component component)
	{
		//		majoritySynsetsCnfGenerator = new MajoritySynsetsCnfGeneratorImpl(
		//				component);
		//		addingFrequencyCnfGenerator = new AddingFrequencyCnfGeneratorImpl(
		//				component);
		addingFrequencyCnfGenerator = new AddingFrequencyCnfGeneratorImplKeepingMiddleVariables(
				component);
	}

	@Override
	public Map<int[], Index> generateCNF(Component component, POS pos)
	{
		Assert.notNull(component);
		this.component = component;
		initializeCnfGenerator(component);

		debug("Begin to generate CNF for component - " + component);

		LOGGER.warn("Begin to generate basic terms for the component. Size of Synsets is["
				+ component.getSynsetsNumber()
				+ "]. "
				+ component.getWordsNumber());

		////////////////////////////////////////////////////////////////////////
		// TODO right now, we generate indices for ALL synsets no matter whether they are majority or not
		Map<int[], Index> result = generateBasicTermForAllSynsets(component,
				false);

		System.out.println("Step 1 ==========================================");
		for (int[] each : result.keySet())
		{
			System.out.println(Arrays.toString(each));
		}
		System.out.println("Step 1 ==========================================");

		////////////////////////////////////////////////////////////////////////
		for (Polarity polarity : Polarity.values())
		{
			Set<Word> wordsForOnePolarity = component
					.getWordsInComponent(polarity);

			debug("Begin to process " + polarity + " words in the component. ");

			for (Word word : wordsForOnePolarity)
			{
				debug(LogHelper.LOG_LAYER_THREE_BEGIN
						+ "Begin to process word - " + word);

				Map<int[], Index> wordExpressionCnfs = null;
				//				if (/*constrainMajoritySynsetsSize &&*/word
				//						.getMajoritySynsets().size() > Config.MAX_SUPPORT_MAJORITY_SYNSETS_NUMBER)
				//				{
				//					debug("Begin to use adding-frequency method to process word["
				//							+ word.getWord()
				//							+ "], since its majority-synsets number exceeds max_support_size["
				//							+ Config.MAX_SUPPORT_MAJORITY_SYNSETS_NUMBER
				//							+ "]. ");

				wordExpressionCnfs = addingFrequencyCnfGenerator.generateCnfs(
						pos, word, polarity);
				//					debug("End to process word[" + word.getWord()
				//							+ "] by using adding-frequency method. ");
				//				}
				//				else
				//				{
				//
				//					wordExpressionCnfs = majoritySynsetsCnfGenerator
				//							.generateCnfs(pos, word, polarity);
				//				}

				if (wordExpressionCnfs != null)
				{
					result.putAll(wordExpressionCnfs);
				}

				debug(LogHelper.LOG_LAYER_THREE_END + "End to process word - "
						+ word);
			}
		}

		////////////////////////////////////////////////////////////////////////
		debug("End to generate CNF for component - " + component);
		//		// DEBUG BEGIN
		//		System.out.println("=================================================");
		//		for (int[] clause : result)
		//		{
		//			for (int i : clause)
		//			{
		//				if (i < 0)
		//				{
		//					System.out.print("(" + i + ") -"
		//							+ Integer.toBinaryString(-i) + "  ");
		//				}
		//				else
		//				{
		//					System.out.print("(" + i + ") " + Integer.toBinaryString(i)
		//							+ "  ");
		//				}
		//			}
		//
		//			System.out.println();
		//		}
		//		System.out.println("=================================================");
		//		// DEBUG END
		return result;
	}

	//	protected void wordHasNoCnf(Word word, Polarity polarity)
	//	{
	//		Assert.isTrue(false, "No CNF generated for word - " + word + "["
	//				+ polarity + "]! ");
	//	}

	private void debug(String message)
	{
		if (LOGGER.isDebugEnabled())
		{
			LOGGER.debug(message);
		}
	}

	/**
	 * 
	 * Step 1 in my report
	 */
	private Map<int[], Index> generateBasicTermForAllSynsets(
			Component component, boolean onlyGeneralMajorities)
	{
		Map<int[], Index> result = new TreeMap<int[], Index>(
				new IntArrayComparator());
		int[] basicCnfTermForEachSynset = null;
		if (onlyGeneralMajorities)
		{
			for (Integer majoritySynsetIndex : component
					.getAllMajoritySynsetIndices())
			{
				basicCnfTermForEachSynset = generateBasicCnfTermForEachSynset(majoritySynsetIndex);
				if (basicCnfTermForEachSynset != null)
				{
					result.put(basicCnfTermForEachSynset, new SynsetIndex(
							majoritySynsetIndex));
				}
			}
		}
		else
		{
			// index begins with 1
			for (int index = 1; index <= component.getSynsetsNumber(); index++)
			{
				basicCnfTermForEachSynset = generateBasicCnfTermForEachSynset(index);
				if (basicCnfTermForEachSynset != null)
				{
					result.put(basicCnfTermForEachSynset,
							new SynsetIndex(index));
				}
			}
		}

		return result;
	}

	/**
	 *  POS.			  NEG.				NEU.
	 * (s1+ AND ¬s1-) OR (¬s1+ AND s1-) OR (¬s1+ AND ¬s1-) ⇔ (¬s1+ OR ¬s1-) 
	 * 
	 * @param synsetIndex
	 * @return
	 */
	protected int[] generateBasicCnfTermForEachSynset(int synsetIndex)
	{
		int[] result = new int[2];
		// notice the order, they are supposed to be sorted. 
		result[0] = not(negativeSymbol(synsetIndex));
		result[1] = not(positiveSymbol(synsetIndex));

		return result;
	}

	@Override
	public int[][] convertCnfIntoIntArrays(Set<int[]> cnf)
	{
		int[][] result = cnf.toArray(new int[cnf.size()][]);

		return result;

	}
}
